package com.example.androidrssreader;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import android.app.Activity;
import android.os.AsyncTask;
import android.view.View;
import android.widget.Toast;

/**
 * RssAsyncReader inherits AsyncTask and does RSS reading from provided URL (as a string
 * parameter in execute method). 
 *   
 * @author gylka
 *
 */

public class RssAsyncReader extends AsyncTask<String, RssItem, Void> {

	private final static int NOT_VALID_URL = 100;
	private final static int URL_HAS_NO_XML = 101;
	private final static int XML_DONT_CONTAIN_RSS = 102;

	private final static int TASK_STARTED = 1;
	private final static int RSS_SUCCESSFULLY_READ = 200;

	private Activity context;
	private RssListAdapter listAdapter;
	private ArrayList<RssItem> rssItems;
	private int taskState;
	
	/**
	 *  
	 * @param context - Activity in which this RssAsyncReader will work, display toasts, update lists
	 * @param rssItems - RSS data that will be updated from this task 
	 */
	
	public RssAsyncReader (Activity context, RssListAdapter listAdapter, ArrayList<RssItem> rssItems) {
		this.context = context;
		this.listAdapter = listAdapter;
		this.rssItems = rssItems;
	}

	@Override
	protected void onPreExecute() {
		this.rssItems.clear();
		this.context.findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
	}
	
	@Override
	protected Void doInBackground(String... params) {
		Document xmldoc = null;
		this.taskState = TASK_STARTED; 
		
		//Reading XML document into DOM-object from URL in params[0] and catching exceptions
		try {
			xmldoc = getXmlDocument(params[0]);
		} catch (MalformedURLException e) {
			this.taskState = NOT_VALID_URL;
			this.publishProgress((RssItem)null);
			return null;
		} catch (SAXException e) {
			this.taskState = URL_HAS_NO_XML;
			publishProgress((RssItem)null);
			return null;
		}
		if (xmldoc == null)
			return null;
		
		// Creating XPath and checking if this XML contains RSS as document element
		XPath xPath = XPathFactory.newInstance().newXPath();
		NodeList nodeList = null;
		try {
			nodeList = (NodeList) xPath.evaluate("/rss", xmldoc, XPathConstants.NODESET);
		} catch (XPathExpressionException e1) {
		}
		if (nodeList.getLength() > 0) {
			NodeList items = null;
			// Getting RSS item nodes
			try {
				items = (NodeList) xPath.evaluate("/rss/channel/item", xmldoc, XPathConstants.NODESET);
			} catch (XPathExpressionException e) {
			}
			for (int i=0; i< items.getLength(); i++) {
				Node item = items.item(i);
				if (item.getNodeType() == Node.ELEMENT_NODE) {
					RssItem rssItem = new RssItem();
					try {
						// Filling created RssItem with read XML data with replaced XML tags, leaving plain text only
						rssItem.title = xPath.evaluate("title", item).replaceAll("\\<.+?\\>", "");
						rssItem.description = xPath.evaluate("description", item).replaceAll("\\<.+?\\>", "");
						rssItem.fullText = xPath.evaluate("fulltext", item).replaceAll("\\<.+?\\>", "");
						rssItem.link = xPath.evaluate("link", item);
						rssItem.pdaLink = xPath.evaluate("pdaLink", item);
						rssItem.pubDate = xPath.evaluate("pubDate", item);
					} catch (XPathExpressionException e) {
					}
					this.publishProgress(rssItem);
				}
			}
			this.taskState = RSS_SUCCESSFULLY_READ;
			this.publishProgress((RssItem)null);
		}
		else
			this.taskState = XML_DONT_CONTAIN_RSS;
			this.publishProgress((RssItem)null);
		return null;
	}
	
	@Override
	protected void onProgressUpdate(RssItem... values) {
		if (values[0] == null) {
			if (this.taskState == NOT_VALID_URL)
				Toast.makeText(context, "Not a valid URL", Toast.LENGTH_LONG).show();
			if (this.taskState == URL_HAS_NO_XML)
				Toast.makeText(context, "URL does not contain XML file", Toast.LENGTH_LONG).show();
			if (this.taskState == URL_HAS_NO_XML)
				Toast.makeText(context, "URL's XML does not contain RSS feed", Toast.LENGTH_LONG).show();
			if (this.taskState == RSS_SUCCESSFULLY_READ)
				Toast.makeText(context, "RSS successfully read", Toast.LENGTH_SHORT).show();
		} else
			this.rssItems.add(values[0]);
		this.listAdapter.notifyDataSetChanged();
	}
	
	@Override
	protected void onPostExecute(Void v) {
		this.context.findViewById(R.id.progressBar).setVisibility(View.INVISIBLE);
	}
	
	/**
	 * Function reads XML document from URL provided by parameter as a String. If address does not contain
	 * XML file or string parameter does not provide proper URL it throws exception
	 *   
	 * @param urlString - URL address
	 * @return XML document  
	 * @throws SAXException - if SAX parser have errors in parsing data from URL address 
	 * @throws MalformedURLException - if urlString can't be converted to URL
	 */
	private static Document getXmlDocument (String urlString) throws SAXException, MalformedURLException {
		HttpURLConnection httpCon = null;
		Document xmldoc = null;
		try {
			URL url = new URL(urlString);
			httpCon = (HttpURLConnection)url.openConnection();
			httpCon.setRequestMethod("GET");
			httpCon.setRequestProperty("Content-Type", "application/rss+xml");
			
			xmldoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(httpCon.getInputStream()));
		}
		catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} 
		finally {
			if(httpCon != null)
				httpCon.disconnect(); 
	    }
		return xmldoc; 
	}
	
}
